#include <curl.hpp>
#include <string>
#include <iostream>

#include "api.h"

struct HeaderData {
  char data[CURL_MAX_HTTP_HEADER+1];
  size_t len;
};

static size_t writeFile(void *contents, size_t size, size_t num, void *udata) {
  auto msg = (std::string*)udata;
	size_t realSize = size * num;

	if (realSize && msg) {
    msg->append((const char*)contents, realSize);
	}

	return realSize;
}

static size_t header_callback(char *buffer, size_t size,
                              size_t nitems, void *userdata)
{
  struct HeaderData *msg = (struct HeaderData*)userdata;
	size_t realSize = size * nitems;

  if (realSize && msg && msg->len < CURL_MAX_HTTP_HEADER) {
    if (msg->len + realSize > CURL_MAX_HTTP_HEADER)
      realSize = CURL_MAX_HTTP_HEADER - msg->len;
    memcpy(msg->data+msg->len, buffer, realSize);
    msg->len += realSize;
    msg->data[msg->len] = 0;
	}

  /* received header is nitems * size long in
   'buffer' NOT ZERO TERMINATED */
  /* 'userdata' is set with CURLOPT_HEADERDATA */
  return size * nitems;
}

static void resp_header_parse(char *buffer, size_t size) {
	if (!buffer || size == 0) return;
	size_t len = size > CURL_MAX_HTTP_HEADER ? CURL_MAX_HTTP_HEADER : size;
	for (size_t i = 0; i < len; ++i) {
		if (buffer[i] == '\r' || buffer[i] == '\n')
			buffer[i] = 0;
	}
}

static const char* str_strnchr(const char *s, uint32_t n, int c) {
  if (!s) return 0;
  while (n-- && *s != '\0') {
    if (*s == c) return s;
    ++s;
  }

  return 0;
}
const char* resp_get_header(const char *buffer, size_t size, const char *field) {
  if (!buffer || size == 0 || !field) return 0;

  size_t count = size > CURL_MAX_HTTP_HEADER ? CURL_MAX_HTTP_HEADER : size;
  const char *val_ptr = 0;
  // skip first line
  const char *value = buffer + strlen(buffer) + 2;
  while (count--) {
    val_ptr = str_strnchr(value, buffer + size - value, ':');
    if (!val_ptr) break;

    if (val_ptr - value != strlen(field)
        || 0 != strncmp(value, field, strlen(field))) {
      value += strlen(value) + 2;
      continue;
    }

    ++val_ptr;

    while ((*val_ptr != '\0') && (*val_ptr == ' ')) {
      ++val_ptr;
    }

    return val_ptr;
  }

  return 0;
}

int main(int argc, char *argv[]) {
  std::string msg = "";
  HeaderData msgHeader = { 0 };

  curl::globalInit(CURL_GLOBAL_ALL);

  CURLcode err = CURLcode::CURL_LAST;
  auto easy = curl::easy();
  if (easy) {
    easy->setopt(CURLoption::CURLOPT_URL, api::lyric);
    easy->setopt(CURLoption::CURLOPT_WRITEFUNCTION, writeFile);
    easy->setopt(CURLoption::CURLOPT_WRITEDATA, &msg);
    easy->setopt(CURLoption::CURLOPT_HEADERFUNCTION, header_callback);
    easy->setopt(CURLoption::CURLOPT_HEADERDATA, &msgHeader);
    err = easy->perform();
    easy->cleanup();
  }
  
  curl::globalCleanup();

  std::cout << msgHeader.data << std::endl;
  std::cout << "----------" << std::endl;
  std::cout << msg << std::endl;
  api::write2file("easy.lrc", msg);
  api::write2file("easy.lrc.header", msgHeader.data, msgHeader.len);
  std::cout << "----------" << std::endl;
  std::cout << "ERROR:" << curl::strError(err) << std::endl;
  std::cout << "CODE:" << (int)err << std::endl;

  std::cout << "----------" << std::endl;
  resp_header_parse(msgHeader.data, msgHeader.len);
 #define __ShowHeader(filed) { const char *msg = resp_get_header(msgHeader.data, msgHeader.len, filed); std::cout << filed ":" << (msg ? msg : "") << std::endl; }
  __ShowHeader("Connection");
  __ShowHeader("Content-Length");
  __ShowHeader("Content-Type");
  __ShowHeader("Date");
  __ShowHeader("Server");
 #undef __ShowHeader
  return 0;
}